package org.xtext.runtime.term;

import java.util.LinkedList;
import java.util.Map;

import org.xtext.runtime.type.IType;
import org.xtext.runtime.type.Type;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.ILogicVisitor;

public class List implements ITerm {
	private ITerm[] terms;
	private IType type;
	
	public List(ITerm[] terms) {
		this.type = Type.listType;
		this.terms = terms;
	}
	
	@Override
	public IType type() {
		return type;
	}

	@Override
	public List copy() {
		ITerm[] trms = new ITerm[terms.length];
		for (int i=0; i<terms.length; i++) {
			trms[i] = terms[i].copy();
		}
		return new List(trms);
	}

	@Override
	public List copy(Map<String, Variable> vars) {
		ITerm[] trms = new ITerm[terms.length];
		for (int i=0; i<terms.length; i++) {
			if (Variable.class.isInstance(terms[i])) {
				Variable v = vars.get(((Variable) terms[i]).identifier());
				if (v == null) {
					v = (Variable) terms[i].copy();
					vars.put(v.identifier(), v);
				}
				trms[i] = v;
			} else {
				trms[i] = terms[i].copy(vars);
			}
		}
		return new List(trms);
	}

	@Override
	public Object value() {
		return asList();
	}

	public String toString() {
		String output = "[";
		for (int i=0; i<terms.length; i++) {
			if (i>0) output += ",";
			output += terms[i];
		}
		return output + "]";
	}

	public ITerm value(int index) {
		return terms[index];
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public java.util.List asList() {
		java.util.List list = new LinkedList();
		for (int i=0; i<terms.length; i++) {
			list.add(Variable.rawValue(terms[i]).value());
		}
		return list;
	}

	public boolean hasFreeVariables() {
		for (ITerm term : terms) {
			if (term.hasFreeVariables()) return true;
		}
		return false;
	}

	@Override
	public ITerm apply(Bindings bindings) {
		for (int i=0; i<terms.length; i++) {
			if (Variable.class.isInstance(terms[i])) {
				Variable var = (Variable) terms[i];
				if (var.isFree()) {
					ITerm term = bindings.getStoredValue(var);
					if (term != null) terms[i] = term;
				} else {
					terms[i] = var.value();
				}
			} else {
				terms[i].apply(bindings);
			}
		}
		return this;
	}

	@Override
	public Object accept(ILogicVisitor visitor) {
		return visitor.visit(this);
	}

	public ITerm[] terms() {
		return this.terms;
	}

	public int size() {
		return terms.length;
	}

	public ITerm termAt(int i) {
		return terms[i];
	}
}
